package fun.codedesign.yinxue.code.php2java.service.visitor.listener;

import fun.codedesign.yinxue.code.engine.antlr.php.PhpParser;
import fun.codedesign.yinxue.code.engine.antlr.php.PhpParserBaseListener;
import fun.codedesign.yinxue.code.php2java.rule.Php2JavaRuleChain;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.tree.ParseTree;
import org.eclipse.jdt.core.dom.*;

import java.util.Stack;
import java.util.concurrent.atomic.AtomicBoolean;

public class Php2JavaListener extends PhpParserBaseListener {

    private final AST ast;
    private final Stack<Object> stack = new Stack<>();

    private final CompilationUnit compilationUnit;
    private final AtomicBoolean packageFlag = new AtomicBoolean(false);

    private final Php2JavaRuleChain rule = new Php2JavaRuleChain();

    {
        ast = AST.newAST(AST.JLS8);
        // 新建一个编译单元
        compilationUnit = ast.newCompilationUnit();
    }

    // namespace phpbb\cron;
    @Override
    public void enterQualifiedNamespaceName(PhpParser.QualifiedNamespaceNameContext ctx) {
        startHandle(ctx);
        String phpNamespace = ctx.getText();
        String packageName = rule.packageReplaceSlashWithDot(phpNamespace);
        PackageDeclaration packageDeclaration = ast.newPackageDeclaration();
        packageDeclaration.setName(ast.newName(packageName.split("\\.")));
        if (packageFlag.compareAndSet(false, true)) {
            compilationUnit.setPackage(packageDeclaration);
        }
        stack.push(packageDeclaration);
    }

    // use common\library\CurlTrait;
    @Override
    public void enterUseDeclaration(PhpParser.UseDeclarationContext ctx) {
        startHandle(ctx);
        // 获取use 后的路径，转换为package
        String originPath = ctx.getChild(1).getText();
        String importLine = rule.useReplaceSlashWithDot(originPath);
        ImportDeclaration importDeclaration = ast.newImportDeclaration();
        importDeclaration.setName(ast.newName(importLine));
        compilationUnit.imports().add(importDeclaration);
    }

    // ClassDeclarationContext: classmanager{protected$tasks=array();protect
    @Override
    public void enterClassDeclaration(PhpParser.ClassDeclarationContext ctx) {
        //  manager -> Manager
        String originTypeName = ctx.identifier().getText();
        String newTypeName = rule.typeName2UpperCaseFirstChar(originTypeName);
        TypeDeclaration typeDeclaration = ast.newTypeDeclaration();
        typeDeclaration.setName(ast.newSimpleName(newTypeName));
        // add in compilationUnit
        compilationUnit.types().add(typeDeclaration);
        // push as parent
        stack.push(typeDeclaration);
    }

    @Override
    public void exitClassDeclaration(PhpParser.ClassDeclarationContext ctx) {
        // 弹出来
        endHandleAndPop(ctx);
    }

    @Override
    public void enterMemberModifier(PhpParser.MemberModifierContext ctx) {
        String modifierText = ctx.getText();
        Object node = stack.peek();
        if (node instanceof BodyDeclaration) {
            BodyDeclaration bodyDeclaration = (BodyDeclaration) node;
            bodyDeclaration.modifiers().add(ast.newModifier(Modifier.ModifierKeyword.toKeyword(modifierText)));
        }
    }


    /**
     * ClassStatementContext: protected$tasks=array();
     * ClassStatementContext: protected$phpbb_root_path;
     * ClassStatementContext: protected$php_ext;
     * ClassStatementContext: publicfunction__construct($tasks,$phpbb_root_path,$php_ext){$this->phpbb_root_path=$phpbb_root_path;$this->php_ext=$php_ext;$this->load_tasks($tasks);}
     *
     * @param ctx the parse tree
     */
    @Override
    public void enterClassStatement(PhpParser.ClassStatementContext ctx) {
        startHandle(ctx);
        // methodName ctx.identifier().getText()
        // 如果为属性变量 ctx.identifier() 为空
        if (ctx.identifier() == null) {
            // 拆到变量初始化
            // 1. 构建一个属性变量声明节点
            FieldDeclaration fieldDeclaration = ast.newFieldDeclaration(ast.newVariableDeclarationFragment());
            // 2. 栈中取出父节点，进行关联
            TypeDeclaration typeDeclaration = (TypeDeclaration) stack.peek();
            typeDeclaration.bodyDeclarations().add(fieldDeclaration);
            // 3. 将自己加入栈中，成为其他节点的父节点
            stack.push(fieldDeclaration);
            return;
        } else {
            // 1. 构建一个方法变量声明节点
            String methodName = ctx.identifier().getText();
            MethodDeclaration methodDeclaration = ast.newMethodDeclaration();

            methodDeclaration.setBody(ast.newBlock());
            methodDeclaration.setName(ast.newSimpleName(methodName));
            // 2. 和父节点建立联系
            TypeDeclaration typeDeclaration = (TypeDeclaration) stack.peek();
            if (methodName.equals("__construct")) {
                // 设置标记位
                methodDeclaration.setProperty(Php2JavaRuleChain.IS_CONSTRUCTOR, Boolean.TRUE);
                methodDeclaration.setName(ast.newSimpleName(typeDeclaration.getName().getIdentifier()));
                methodDeclaration.setConstructor(true);
            }
            typeDeclaration.bodyDeclarations().add(methodDeclaration);
            // 3. 将自己压入栈中，成为其他节点的父节点
            stack.push(methodDeclaration);
        }
    }

    @Override
    public void exitClassStatement(PhpParser.ClassStatementContext ctx) {
        endHandleAndPop(ctx);
    }

    @Override
    public void enterMethodBody(PhpParser.MethodBodyContext ctx) {
        super.enterMethodBody(ctx);
    }

    @Override
    public void exitMethodBody(PhpParser.MethodBodyContext ctx) {
        super.exitMethodBody(ctx);
    }

    @Override
    public void enterModifier(PhpParser.ModifierContext ctx) {
        super.enterModifier(ctx);
    }

    @Override
    public void enterVariableInitializer(PhpParser.VariableInitializerContext ctx) {
        startHandle(ctx);
        // 1. 构建一个变量节点
        String varName = ctx.VarName().getText();
        String newVarName = rule.varReplaceDollar(varName);

        String initStatement = null;
        if (ctx.constantInitializer() != null) {
            initStatement = ctx.constantInitializer().getText();
        }

        VariableDeclarationFragment variableDeclarationFragment = ast.newVariableDeclarationFragment();
        variableDeclarationFragment.setName(ast.newSimpleName(newVarName));

        // 如果右侧是数组
        if (initStatement != null && initStatement.equals("array()")) {
            ArrayCreation creation = ast.newArrayCreation();
            ArrayInitializer arrayInitializer = ast.newArrayInitializer();
            ArrayType type = ast.newArrayType(ast.newSimpleType(ast.newName(rule.thinkVarType(newVarName, initStatement))));
            creation.setInitializer(arrayInitializer);
            creation.setType(type);
            variableDeclarationFragment.setInitializer(creation);
        }
        // 2. 建立和父节点之间的关系
        Object node = stack.peek();
        // 2.1 如果是声明变量，则设置进去
        if (node instanceof FieldDeclaration) {
            FieldDeclaration fieldDeclaration = (FieldDeclaration) node;
            // 设置 fieldDeclaration类型 设置 variableDeclarationFragment变量及初始化语句， 替换掉初始化时的变量 variableDeclarationFragment
            fieldDeclaration.fragments().set(0, variableDeclarationFragment);
            // TODO 右侧推断到左侧
            // fieldDeclaration.setType(type);
        } else if (node instanceof MethodDeclaration) {
            MethodDeclaration methodDeclaration = (MethodDeclaration) node;
            SingleVariableDeclaration singleVariableDeclaration = ast.newSingleVariableDeclaration();
            // TODO 确认类型，后置循环完后需要在上下文里取到他的类型,  默认使用 Object
            singleVariableDeclaration.setType(ast.newSimpleType(ast.newName(rule.thinkVarType(newVarName))));
            singleVariableDeclaration.setName(ast.newSimpleName(newVarName));
            methodDeclaration.parameters().add(singleVariableDeclaration);
            // 3. 将自己压入栈中
        }
        stack.push(variableDeclarationFragment);
    }

    @Override
    public void exitVariableInitializer(PhpParser.VariableInitializerContext ctx) {
        endHandleAndPop(ctx);
    }

    // ArgumentsContext: ($task,$this->phpbb_root_path,$this->php_ext)
    @Override
    public void enterArguments(PhpParser.ArgumentsContext ctx) {
        startHandle(ctx);
    }

    @Override
    public void exitArguments(PhpParser.ArgumentsContext ctx) {
//        endHandleAndPop(ctx);
    }

    @Override
    public void enterInnerStatement(PhpParser.InnerStatementContext ctx) {
        super.enterInnerStatement(ctx);
    }

    @Override
    public void exitInnerStatement(PhpParser.InnerStatementContext ctx) {
        super.exitInnerStatement(ctx);
    }

    // 赋值语句顺序 enterAssignmentExpression->enterAssignable->enterChainExpression
    @Override
    public void enterAssignmentExpression(PhpParser.AssignmentExpressionContext ctx) {
        Assignment assignment = ast.newAssignment();
        assignment.setOperator(Assignment.Operator.ASSIGN);
        // 将 Expression 表达式转换为 Statement
        Object node = stack.peek();
        if (node instanceof MethodDeclaration) {
            MethodDeclaration methodDeclaration = (MethodDeclaration) node;
            ExpressionStatement expressionStatement = ast.newExpressionStatement(assignment);
            methodDeclaration.getBody().statements().add(expressionStatement);
        }
        // 这里放赋值语句，不放expressionStatement
        stack.push(assignment);
    }

    @Override
    public void exitAssignmentExpression(PhpParser.AssignmentExpressionContext ctx) {
        endHandleAndPop(ctx);
    }

    // 赋值语句的左侧
    @Override
    public void enterAssignable(PhpParser.AssignableContext ctx) {
        startHandle(ctx);
        String assignVarText = ctx.getText();
        if (assignVarText.startsWith("$this")) {
            FieldAccess fieldAccess = ast.newFieldAccess();
            String fieldName = rule.replaceThisAndArrayWithWhiteSpace(assignVarText);
            fieldAccess.setExpression(ast.newThisExpression());
            fieldAccess.setName(ast.newSimpleName(fieldName));
            Object node = stack.peek();
            if (node instanceof Assignment) {
                Assignment assignment = (Assignment) node;
                assignment.setLeftHandSide(fieldAccess);
            }
        }
    }


    // $this->phpbb_root_path = $phpbb_root_path; 这种赋值语句不会进来，只会取 phpbb_root_path
    // $phpbb_root_path
    // $php_ext
    // $this->load_tasks($tasks); 表达式语句
    @Override
    public void enterChainExpression(PhpParser.ChainExpressionContext ctx) {
        // TODO 这里有点乱，情况比较复杂，需要重点写，考虑各种 上游情况
        startHandle(ctx);
        String chainText = ctx.getText();
        String newChainText = rule.varReplaceDollar(chainText);
        Object node = stack.peek();
        // 根据是否包含末尾为括号来确定，该链路是否为 变量链路还是方法调用链路
        if (chainText.endsWith(")")) {
            MethodInvocation methodInvocation = ast.newMethodInvocation();
            if (node instanceof Assignment) {
                Assignment assignment = (Assignment) node;
                assignment.setRightHandSide(methodInvocation);
            } else if (node instanceof MethodDeclaration) {
                MethodDeclaration methodDeclaration = (MethodDeclaration) node;
                methodDeclaration.getBody().statements().add(ast.newExpressionStatement(methodInvocation));
            }
            stack.push(methodInvocation);
            // 不包含点就是普通变量
        } else if (!newChainText.contains(".")) {
            // 如果是赋值语句变量
            if (chainText.startsWith("$this->")) {
                String rightFieldName = chainText;
                FieldAccess fieldAccess = ast.newFieldAccess();
                String fieldName = rule.replaceThisAndArrayWithWhiteSpace(rightFieldName);
                fieldAccess.setExpression(ast.newThisExpression());
                fieldAccess.setName(ast.newSimpleName(fieldName));
                if (node instanceof Assignment) {
                    Assignment assignment = (Assignment) node;
                    assignment.setRightHandSide(fieldAccess);
                }
            } else {
                String rightFieldName = rule.varReplaceDollar(chainText);
                Name name = ast.newName(rightFieldName);
                // TODO 关联
                if (node instanceof Assignment) {
                    Assignment assignment = (Assignment) node;
                    assignment.setRightHandSide(name);
                }
            }
//            FieldAccess fieldAccess = ast.newFieldAccess();
            stack.push(new Object());
            // 包含点 . 的，分为包含 . 和 )
        } else if (newChainText.contains(".") && newChainText.contains("(") && newChainText.contains(")")) {
            // TODO
            stack.push(new Object());
        }

//        if (node instanceof ReturnStatement) {
//            ReturnStatement returnStatement = (ReturnStatement) node;
//            returnStatement.setExpression(ast.newSimpleName(chainText));
//        }
    }

    @Override
    public void exitChainExpression(PhpParser.ChainExpressionContext ctx) {
        endHandleAndPop(ctx);
    }

    // ChainContext: $this->phpbb_root_path
    @Override
    public void enterChain(PhpParser.ChainContext ctx) {
        startHandle(ctx);
        String chainText = ctx.getText();
        Object node = stack.peek();
        if (node instanceof Assignment) {
            Assignment assignment = (Assignment) node;
            if (chainText.startsWith("$this")) {
                FieldAccess fieldAccess = ast.newFieldAccess();
                String fieldName = rule.replaceThisAndArrayWithWhiteSpace(chainText);
                fieldAccess.setExpression(ast.newThisExpression());
                fieldAccess.setName(ast.newSimpleName(fieldName));
                assignment.setLeftHandSide(fieldAccess);
            }
        }

        if (node instanceof MethodInvocation) {
            MethodInvocation methodInvocation = (MethodInvocation) node;
            if (chainText.startsWith("$this")) {
                methodInvocation.setExpression(ast.newThisExpression());
            }
            chainText = chainText.replace("$this->", "");
            // 剔除 ( ) 但是在多个调用时无效
            chainText = chainText.replaceAll("\\(.*\\)", "");
            // TODO 作为参数传递的情况 $task->is_ready()
            //methodInvocation.setName(ast.newSimpleName(chainText));
        }
    }


    @Override
    public void enterActualArgument(PhpParser.ActualArgumentContext ctx) {
        startHandle(ctx);
        // 1.
        String arguText = ctx.getText();
        String newValName = rule.varReplaceDollar(arguText);

        // 2.
        Object node = stack.peek();
        if (node instanceof MethodDeclaration) {
            MethodDeclaration methodDeclaration = (MethodDeclaration) node;

            SingleVariableDeclaration singleVariableDeclaration = ast.newSingleVariableDeclaration();
            singleVariableDeclaration.setName(ast.newSimpleName(rule.thinkVarType(newValName)));
            singleVariableDeclaration.setInitializer(ast.newName(newValName));

            methodDeclaration.parameters().add(singleVariableDeclaration);
        }
        if (node instanceof MethodInvocation) {
            MethodInvocation methodInvocation = (MethodInvocation) node;
            methodInvocation.arguments().add(ast.newName(newValName));
        }
        // 3.
//        stack.push(variableDeclaration);
    }

    @Override
    public void exitActualArgument(PhpParser.ActualArgumentContext ctx) {
//        endHandleAndPop(ctx);
    }

    @Override
    public void enterReturnStatement(PhpParser.ReturnStatementContext ctx) {
        startHandle(ctx);
        ReturnStatement returnStatement = ast.newReturnStatement();
//        returnStatement.setExpression();
        Object node = stack.peek();
        if (node instanceof MethodDeclaration) {
            MethodDeclaration methodDeclaration = (MethodDeclaration) node;
            methodDeclaration.getBody().statements().add(returnStatement);
//            stack.push(returnStatement);
        }

        for (ParseTree tree : ctx.children) {
            if (tree instanceof PhpParser.ChainExpressionContext) {
//                PhpParser.ChainExpressionContext context = (PhpParser.ChainExpressionContext) tree;
//                String returnStatment = context.getText();
//                if (returnStatment.contains("->")) {
//                    // TODO
//                    System.err.println("带->标参数" + returnStatment);
//                    return;
//                }
//                SimpleType simpleType = ast.newSimpleType(ast.newName(returnStatment));
//                MethodDeclaration declaration = (MethodDeclaration) stack.peek();
//                declaration.setReturnType2(simpleType);
            }
        }
    }

    // return类型应该在进入的时候还是，输出的
    @Override
    public void exitReturnStatement(PhpParser.ReturnStatementContext ctx) {
        super.exitReturnStatement(ctx);
    }


    void startHandle(ParserRuleContext ctx) {
        String info = ctx.getClass().getSimpleName() + ": " + ctx.getText();
        System.out.println("开始处理:" + info);
    }

    void endHandleAndPop(ParserRuleContext ctx) {
        String info = ctx.getClass().getSimpleName() + ": " + ctx.getText();
        System.out.println("结束处理:" + info);
        Object obj = stack.pop();
        System.out.println("出栈节点信息:" + obj);
    }

    public CompilationUnit getCompilationUnit() {
        return compilationUnit;
    }
}
